﻿$import( "SmitearWebGL.Objects.Viewport" );
$import( "SmitearWebGL.Objects.Entity" );
$import( "SmitearWebGL.Core.Geometry.Frustum" );

SmitearWebGL.Objects.Camera = SmitearWebGL.Objects.Entity.extend(
{
	/******** constructor ********/
	
	init : function()
	{
		/******** private property ********/
		
		this._target = null;
		this._viewMatrix = null;
		this._frustum = null;
		this._fovy = 0.0;
		this._aspect = 0.0;
		this._nearClip = 0.0;
		this._farClip = 0.0;
		this._parmStatus = null;
		this._frustumBuffer = null;
		
		this._super( name );
		this._registClass( "SmitearWebGL.Objects.Camera" );
	}
	,
	
	/******** public method ********/
	
	getFrustumBuffer : function()
	{
		return this._frustumBuffer;
	}
	,
	setTarget : function( target )
	{
		this._target = target;
		this._parmStatus.setBit( SmitearWebGL.Objects.Camera.nTargetDirty, true );
	}
	,
	setNearClip : function( value )
	{
		if ( this._nearClip == value )
			return;
			
		this._nearClip = value;
		this._status.setBit( SmitearWebGL.Objects.Entity.nProjParamDirty, true );
		this._parmStatus.setBit( SmitearWebGL.Objects.Camera.nNearDirty, true );
	}
	,
	setFarClip : function( value )
	{
		if ( this._farClip == value )
			return;
			
		this._farClip = value;
		this._status.setBit( SmitearWebGL.Objects.Entity.nProjParamDirty, true );
		this._parmStatus.setBit( SmitearWebGL.Objects.Camera.nFarDirty, true );
	}
	,
	setFovY : function( fov )
	{
		if ( this._fovy == fov )
			return;
			
		this._fovy = fov;
		this._status.setBit( SmitearWebGL.Objects.Entity.nProjParamDirty, true );
		this._parmStatus.setBit( SmitearWebGL.Objects.Camera.nFovDirty, true );
	}
	,
	setAspect : function( aspect )
	{
		this._aspect = aspect;
		this._parmStatus.setBit( SmitearWebGL.Objects.Camera.nAspectDirty, true );
	}
	,
	getNearClip : function()
	{
		return this._nearClip;
	}
	,
	getFarClip : function()
	{
		return this._farClip;
	}
	,
	getFrustum : function()
	{
		return this._frustum;
	}
	,
	getViewMatrix : function()
	{
		return this._viewMatrix;
	}
	,
	getProjectionMatrix : function()
	{
		return this._projectionMatrix;
	}
	,
	getInvProjectionMatrix : function()
	{
		return this._invProjectionMatrix;
	}
	,
	updateTransform : function()
	{	
		var parentDirty = false;
		if ( this._parent && this._parent.isTransformDirty()  )
		{
			if ( ! this._parent.isTransformed() )
				this._parent.updateTransform();
			parentDirty = true;
		}
			
		if ( this.isTransformDirty() || parentDirty )
		{
			if ( this._target != null )
			{
				if ( this._target.isEqualTo( this._position ) )
					$WARNING( "Look at error!" );
				
				this._worldMatrix.identity();
				this._worldMatrix.calculateMultiply( SmitearWebGL.Core.Math.Matrix4x4.translation( this._position.x, this._position.y, this._position.z ), this._worldMatrix );
				if ( this._parent && this._parent.isTransformDirty() )
				{
					this._worldMatrix.calculateMultiply( this._parent._worldMatrix, this._worldMatrix );
				}
				SmitearWebGL.Core.Math.Matrix4x4.lookAtRH(this._viewMatrix,
															this._worldMatrix.n41,
															this._worldMatrix.n42,
															this._worldMatrix.n43,
															this._target.x,
															this._target.y,
															this._target.z,
															0.0, 1.0, 0.0 );
				this._worldMatrix.calculateInverse4x4( this._viewMatrix );
			}
			else
			{
				this._quaternion.setFromYawPitchRoll( this._rotation.y * SmitearWebGL.Core.Math.toRADIANS,
													this._rotation.x * SmitearWebGL.Core.Math.toRADIANS,
													this._rotation.z * SmitearWebGL.Core.Math.toRADIANS );

				this._worldMatrix.identity();
				this._worldMatrix.calculateMultiply( SmitearWebGL.Core.Math.Matrix4x4.scale( this._scale.x, this._scale.y, this._scale.z ), this._worldMatrix );
				this._worldMatrix.calculateMultiply( this._quaternion.getMatrix(), this._worldMatrix );
				this._worldMatrix.calculateMultiply( SmitearWebGL.Core.Math.Matrix4x4.translation( this._position.x, this._position.y, this._position.z ), this._worldMatrix );
				
				if ( parentDirty )
				{
					this._worldMatrix.calculateMultiply( this._parent._worldMatrix, this._worldMatrix );
				}
				
				this._viewMatrix.calculateInverse4x4( this._worldMatrix );
			}
		}
		
		if ( this._status.testBit( SmitearWebGL.Objects.Entity.nProjParamDirty ) )
		{
			SmitearWebGL.Core.Math.Matrix4x4.makePerspectiveFov( this._projectionMatrix, this._fovy, this._aspect, this._nearClip, this._farClip );
			this._invProjectionMatrix.calculateInverse4x4( this._projectionMatrix );
			
			this._frustum.transformScreenCoord( this._invProjectionMatrix );
			this._buildFrustumBuffer( [4, 5, 6, 7] );
		}
			
		if ( this.isTransformDirty() || parentDirty )
		{
			this._frustum.transformWorldCoord( this._worldMatrix );
			
			this._status.setBit( SmitearWebGL.Objects.Entity.nTransformed, true );
			this._status.setBit( SmitearWebGL.Objects.Entity.nViewDirty, true );
		}
		
		if ( this._status.testBit( SmitearWebGL.Objects.Camera.nNearDirty ) || 
			this._status.testBit( SmitearWebGL.Objects.Camera.nFarDirty ) )
		{
			this.context.gl.depthRange( this._nearClip, this._farClip );
		}
	}
	,
	updateAfterRender : function()
	{
		this._super();
		
		this._parmStatus.clear();
	}
	,
	_buildFrustumBuffer : function( frustumCoordIndex )
	{
		if ( this._frustumBuffer )
			this._frustumBuffer.release();
		
		var data = new Array( frustumCoordIndex.length * 3 );
		var coord;
		for ( var i = 0, j = 0; i < frustumCoordIndex.length ; ++ i, j += 3 )
		{
			coord = this._frustum.viewSpaceCoords[frustumCoordIndex[i]];
			data[j] = coord.x;
			data[j+1] = coord.y;
			data[j+2] = coord.z;
		}
		
		this._frustumBuffer = this.context.resourceManager.createResource( "__" + new Date().getTime() + "_frustumCoordsBuffer__" , SmitearWebGL.Resources.VertexBuffer );
		this._frustumBuffer.bindData( data );
		this._frustumBuffer.createElementDescription( 3, 0, 0, SmitearWebGL.Resources.Buffer.DataType.FLOAT, SmitearWebGL.Resources.Buffer.AttribLocation.FRUSTUMCOORDS );
	}
	,
	_createResource : function( params )
	{
		this._super();
		
		this._target = null;
		this._frustumBuffer = null;
		this._viewMatrix = $M4x4();
		this._frustum = new SmitearWebGL.Core.Geometry.Frustum();
		this._fovy = params.fov;
		this._aspect = params.aspect;
		this._nearClip = params.near;
		this._farClip = params.far;
		this._parmStatus = new SmitearWebGL.Core.Math.BitFlags();
		this._parmStatus.set( ( 1 << SmitearWebGL.Objects.Camera.nTotal ) - 1 );
		this._status.setBit( SmitearWebGL.Objects.Entity.nProjParamDirty, true );
	}
	,
	_destroyResource : function()
	{
		this._super();
		
		this._frustum.release();
		this._frustumBuffer.release();
		
		this._target = null;
		this._viewMatrix = null;
		this._frustum = null;
		this._parmStatus = null;
	}
}
);

SmitearWebGL.Objects.Camera.nNearDirty = 0;
SmitearWebGL.Objects.Camera.nFarDirty = 1;
SmitearWebGL.Objects.Camera.nFovDirty = 2;
SmitearWebGL.Objects.Camera.nTargetDirty = 3;
SmitearWebGL.Objects.Camera.nAspectDirty = 4;
SmitearWebGL.Objects.Camera.nTotal = 5;